#include <sys/epoll.h>
#include <iostream>
#include <memory>
#include <string>

#include "Log.hpp"
#include "Socket.hpp"

#define revs_num 64
using namespace LogMudule;
using namespace SocketModule;

// 先编写一个epoll服务器，再加入LT/ET模式
namespace _ns_epoll
{
    class EpollServer
    {
    public:
        EpollServer(uint16_t port) : _listen_sock_ptr(std::make_unique<TcpSocket>()), _port(port), _isrunning(false)
        {
            _listen_sock_ptr->BuildTcpSocketMethod(_port);
        }
        void Init()
        {
            _epfd = ::epoll_create(32); // 创建epoll模型，epoll在进程看来就是一个文件描述符
            struct epoll_event events;
            events.events = EPOLLIN;
            events.data.fd = _listen_sock_ptr->Fd();

            int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listen_sock_ptr->Fd(), &events); // 插入成功，返回zero
            if (n < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                exit(EPOLL_CTL_ERR);
            }
        }

        void Loop()
        {
            _isrunning = true;
            while (_isrunning)
            {
                // epoll进行事件的等待
                int timeout = -1; // 阻塞式等待

                int n = ::epoll_wait(_epfd, _revs, revs_num, timeout); // revs_num是一次从最多获取的文件描述符个数
                switch (n)
                {
                case 0:
                    LOG(LogLevel::INFO) << "time out...";
                    break;
                case -1:
                    LOG(LogLevel::FATAL) << "epoll_wait_error";
                    break;
                default:
                    LOG(LogLevel::INFO) << "epoll_wait_success";
                    DisPatcher(n); // 进行事件派发
                    break;
                }
            }
            _isrunning = false;
        }

        void Accept()
        {
            InetAddr client;
            int newfd = _listen_sock_ptr->Accepter(&client);
            if (newfd < 0)
            {
                LOG(LogLevel::ERROR) << "accept error";
            }
            else
            {
                // 将新的sockfd添加到epoll中
                std::cout << "获得了一个新的链接: " << newfd << " client info: " << client.Addr() << std::endl;
                // 我们需要将这个新的newfd添加给epoll，让epoll帮我们进行监管。
                struct epoll_event ev;
                ev.events = EPOLLIN;
                ev.data.fd = newfd;
                int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, newfd, &ev);
                if (n < 0)
                {
                    LOG(LogLevel::ERROR) << "epoll_ctl error";
                    close(newfd);
                }
                LOG(LogLevel::DEBUG) << "epoll_ctl success: " << newfd;
            }
        }

        void Recver(int index)
        {
            int fd = _revs[index].data.fd;
            char buffer[1024];
            int n = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                std::string message = "echo# ";
                message += buffer;
                ::send(fd, message.c_str(), message.size(), 0);
            }
            else if (n == 0)
            {
                LOG(LogLevel::DEBUG) << "客户端退出, sockfd: " << fd;

                // 把fd从epoll中移除, 必须保证fd是一个合法的fd.
                int m = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
                if (m < 0)
                {
                    LOG(LogLevel::ERROR) << "epoll_ctl error";
                    return;
                }
                LOG(LogLevel::DEBUG) << "epoll_ctl success: " << fd;
                close(fd);
            }
            else
            {
                LOG(LogLevel::ERROR) << "recv error";
                // 把fd从epoll中移除, 必须保证fd是一个合法的fd.
                int m = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
                if (m < 0)
                {
                    LOG(LogLevel::ERROR) << "epoll_ctl error";
                    return;
                }
                LOG(LogLevel::DEBUG) << "epoll_ctl success: " << fd;
                close(fd);
            }
        }

        void DisPatcher(int num)
        {
            for (int i = 0; i < num; i++)
            {
                int fd = _revs[i].data.fd;
                int revents = _revs[i].events;

                if (fd == _listen_sock_ptr->Fd())
                {
                    if (revents == EPOLLIN)
                    {
                        Accept();
                    }
                }
                else
                {
                    if (revents == EPOLLIN)
                    {
                        Recver(i);
                    }
                }
            }
        }

        ~EpollServer()
        {
        }

    private:
        std::unique_ptr<Socket> _listen_sock_ptr; // 智能指针指向封装的listen_socket
        int _epfd;                                // epoll模型
        struct epoll_event _revs[revs_num];

        bool _isrunning;
        uint16_t _port;
    };
}
